﻿/*
SINACOR - BMF&Bovespa
Arquivo: Importacao.cs
Criado em: 28-jun-2010 14:45:43
Arquiteto Responsável: p-wgeneroso
*/
using System;
using System.Collections.Generic;
using System.Text;
using Sinacor.Infra.Core.ProcessManager.Server;
using Sinacor.Infra.Common.Exceptions;
using Sinacor.Infra.Service.ExceptionHandling;
using PSE.ProcessManager.Common.Process;
using PSE.Framework.Communication.Adapters.File;
using PSE.Framework.Communication.Client.File.FlatFile;
using System.Threading;
using Sinacor.Infra.Service.Validation;
using PSE.ProcessManager.Server.Process.Security;
using Sinacor.Infra.Core.ProcessManager.Server.Security;

namespace Sinacor.Process.Bovespa.Alocacao.Server.ProcessLogic
{
    public class Importacao : BaseProcessLogic
    {
        #region Atributos

        private ManualResetEvent _importFinishControl;
        private FlatFileInputClient _client = null;
        private ProcessEntity.ErroAlocacaoHeader _header;
        private ProcessEntity.ErroAlocacaoTrailer _trailer;
        private List<ProcessEntity.ErroAlocacaoDetail> _alocacoes;
        private int _erroCodigoCorretoraDivergente = 109792;
        private int _erroDataPregaoForaCalendario = 109802;
        private int _erroArquivoInvalido = 109798;
        private int _erroQuantidadeRegistrosInvalido = 109801;
        private Translator.LayoutToProcessEntity.Alocacao _translatorAlocacao;
        private bool _erroLeituraArquivo = false;

        #endregion

        #region Eventos

        /// <summary>
        /// Evento disparado ao concluir uma importação de arquivo
        /// </summary>
        public event EventHandler ImportacaoIniciada;
        public event EventHandler<ProcessEventArgs<ProcessEntity.ImportacaoResultado>> ImportacaoConcluida;

        #endregion

        #region Construtor

        /// <summary>
        /// Construtor padrão
        /// </summary>
        public Importacao()
        {
            _translatorAlocacao = new Translator.LayoutToProcessEntity.Alocacao();
        }

        #endregion

        #region Propriedades

        public int ErroCodigoCorretoraDivergente
        {
            get { return _erroCodigoCorretoraDivergente; }
            set { _erroCodigoCorretoraDivergente = value; }
        }

        public int ErroDataPregaoForaCalendario
        {
            get { return _erroDataPregaoForaCalendario; }
            set { _erroDataPregaoForaCalendario = value; }
        }

        public int ErroArquivoInvalido
        {
            get { return _erroArquivoInvalido; }
            set { _erroArquivoInvalido = value; }
        }

        public int ErroQuantidadeRegistrosInvalido
        {
            get { return _erroQuantidadeRegistrosInvalido; }
            set { _erroQuantidadeRegistrosInvalido = value; }
        }

        public bool ErroLeituraArquivo
        {
            get { return _erroLeituraArquivo; }
            set { _erroLeituraArquivo = value; }
        }

        #endregion

        #region EventHandler

        /// 
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _client_FileClosed(object sender, FileClosedEventArgs e)
        {
            FecharClient();
        }

        /// 
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _client_FileOpened(object sender, FileOpenedEventArgs e)
        {
            if (!IsAborted)
            {
                _header = null;
                _trailer = null;

                if (_alocacoes == null)
                    _alocacoes = new List<ProcessEntity.ErroAlocacaoDetail>();

                _alocacoes.Clear();
            }
        }

        /// 
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _client_RecordRead(object sender, RecordEventArgs e)
        {
            try
            {
                if (e.Record is Layout.ErroAlocacaoHeader)
                {
                    _header = _translatorAlocacao.ToErroAlocacaoHeader((Layout.ErroAlocacaoHeader)e.Record);
                }
                else if (e.Record is Layout.ErroAlocacaoDetail)
                {
                    _alocacoes.Add(_translatorAlocacao.ToErroAlocacaoDetail((Layout.ErroAlocacaoDetail)e.Record));
                }
                else if (e.Record is Layout.ErroAlocacaoTrailer)
                {
                    _trailer = _translatorAlocacao.ToErroAlocacaoTrailer((Layout.ErroAlocacaoTrailer)e.Record);
                }
            }
            catch (Exception ex)
            {
                this.HandleException(ex, true);

                FecharClient();
            }
        }

        /// 
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _client_RecordReadError(object sender, RecordReadErrorEventArgs e)
        {
            _erroLeituraArquivo = true;

            this.HandleException(new BusinessException(ErroArquivoInvalido, e.Exception), true);

            FecharClient();
        }

        private void _client_FileCanceled(object sender, FileCanceledEventArgs e)
        {
            FecharClient();
        }

        #endregion

        #region Métodos Públicos

        /// 
        /// <param name="entity"></param>
        public void ImportarAlocacoes(ProcessEntity.Parameter.FiltroImportacaoAlocacao entity)
        {
            ResourceAccess.ImportacaoExportacao raImportacaoExportacao = null;
            ResourceAccess.Importacao raImportacao = null;
            Communication.Calendario communicationCalendario;
            List<ProcessEntity.Calendario> calendario;
            SinacorValidation validation = null;
            decimal porcentagem = 5;
            decimal porcentagemItem;
            int atualizarStatus;
            ProcessEntity.ImportacaoResultado resultado;

            if (ImportacaoIniciada != null)
            {
                ImportacaoIniciada(this, new EventArgs());
            }

            if (!IsAborted)
            {
                validation = new SinacorValidation();
                validation.Validate<ProcessEntity.Parameter.FiltroImportacaoAlocacao>(entity);

                if (!IsAborted
                    && !_erroLeituraArquivo)
                {
                    AtualizaStatus(porcentagem, "Bovespa.ImportacaoExportacao.Label.LendoArquivo");
                    LerArquivoAlocacao(entity.NomeArquivo);

                    porcentagem = 10;
                    AtualizaStatus(porcentagem, "Bovespa.ImportacaoExportacao.Label.ValidandoArquivo");

                    if (_trailer.TotalRegistros != _alocacoes.Count + 2)
                        throw new BusinessException(ErroQuantidadeRegistrosInvalido);

                    communicationCalendario = new Communication.Calendario();
                    calendario = communicationCalendario.ConsultarCalendarios();

                    if (calendario.FindIndex(delegate(ProcessEntity.Calendario compare)
                    {
                        return compare.DataPregao.Date == entity.DataPregao.Date;
                    }) == -1)
                    {
                        throw new BusinessException(ErroDataPregaoForaCalendario);
                    } 

                    raImportacaoExportacao = new ResourceAccess.ImportacaoExportacao();

                    if (_header.CodigoDestino != raImportacaoExportacao.ObterCodigoEmpresaBolsa())
                        throw new BusinessException(ErroCodigoCorretoraDivergente);                    

                    if (!IsAborted)
                    {
                        raImportacao = new ResourceAccess.Importacao();
                        raImportacao.LimparTabelaImportacao();

                        if (!IsAborted)
                        {
                            porcentagemItem = (_alocacoes.Count > 0) ? 70M / Convert.ToDecimal(_alocacoes.Count) : 70;

                            atualizarStatus = (_alocacoes.Count < 100) ? 10 : 100;

                            BeginTransaction(AuthenticationContext<SinacorAuthenticationData>.Current.AuthenticationData.CompanyId);

                            for (int i = 0; i < _alocacoes.Count; i++)
                            {
                                if (IsAborted)
                                    break;

                                porcentagem += porcentagemItem;
                                raImportacao.ImportarAlocacao(_alocacoes[i]);

                                if (i % atualizarStatus == 0)
                                {
                                    AtualizaStatus(porcentagem, "Bovespa.ImportacaoExportacao.Label.ImportandoNegocios");
                                }

                                if (i % 100 == 0)
                                {
                                    CommitTransaction();
                                    BeginTransaction(AuthenticationContext<SinacorAuthenticationData>.Current.AuthenticationData.CompanyId);
                                }
                            }

                            if (!IsAborted)
                            {
                                CommitTransaction();

                                porcentagem = 80;
                                AtualizaStatus(porcentagem, "Bovespa.ImportacaoExportacao.Label.ProcessandoNegocios");

                                if (!IsAborted)
                                {
                                    raImportacao.GravarLogImportacao(entity, _trailer);

                                    porcentagem = 90;
                                    AtualizaStatus(porcentagem, "Bovespa.ImportacaoExportacao.Label.FinalizandoProcessamento");
                                }
                            }
                        }
                    }
                }
            }

            if (!_erroLeituraArquivo)
            {
                if (!IsAborted)
                {
                    CommitTransaction();

                    if (ImportacaoConcluida != null)
                    {
                        resultado = new ProcessEntity.ImportacaoResultado();
                        resultado.QuantidadeRegistrosImportados = _alocacoes.Count;
                        ImportacaoConcluida(this, new ProcessEventArgs<ProcessEntity.ImportacaoResultado>(resultado));
                    }
                }
                else
                {
                    RollbackTransaction();
                }
            }
        }

        #endregion

        #region Métodos Privados

        /// 
        /// <param name="nomeArquivo"></param>
        private void LerArquivoAlocacao(string nomeArquivo)
        {
            _importFinishControl = new ManualResetEvent(false);

            _client = new FlatFileInputClient();

            _client.Open("ImportacaoEESC");

            _client.FileOpened += new EventHandler<FileOpenedEventArgs>(_client_FileOpened);
            _client.FileClosed += new EventHandler<FileClosedEventArgs>(_client_FileClosed);
            _client.RecordRead += new EventHandler<RecordEventArgs>(_client_RecordRead);
            _client.RecordReadError += new EventHandler<RecordReadErrorEventArgs>(_client_RecordReadError);
            _client.FileCanceled += new EventHandler<FileCanceledEventArgs>(_client_FileCanceled);

            _client.BeginImport(new FileReceiveParameter(System.IO.Path.GetFileName(nomeArquivo)));

            _importFinishControl.WaitOne();
        }

        private void FecharClient()
        {
            if (_client != null)
            {
                if (_client.Status == PSE.Framework.Communication.CommunicationStatus.Opened)
                {
                    _client.EndImport();
                    _client.Close();
                }

                _client = null;
            }

            if (_importFinishControl != null)
                _importFinishControl.Set();
        }

        private void AtualizaStatus(decimal progress, string resourceKey)
        {
            this.Status.Progress = Convert.ToInt32(Math.Round(progress, 0, MidpointRounding.ToEven));
            this.Status.Step = resourceKey;
            this.SendStatus();
        }

        #endregion
    }
}